package com.example.ramcache.orm.impl;

import com.example.ramcache.IEntity;
import com.example.ramcache.PerPersist;
import com.example.ramcache.orm.Accessor;
import com.example.ramcache.orm.EntityMetadata;
import org.hibernate.internal.StatelessSessionImpl;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.Query;

import javax.annotation.PostConstruct;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * @author frank
 * {@link Accessor} 的 Hibernate 实现
 */
@SuppressWarnings("unchecked")
public class HibernateAccessor extends HibernateHelper implements Accessor {

    /** 实体的元信息 */
    private Map<String, EntityMetadata> metadatas = new HashMap<>();

    @PostConstruct
    public void postConstruct() {
        super.init();
        Map<String, EntityPersister> entityPersisters = getSessionFactory().getMetamodel().entityPersisters();
        for (EntityPersister entityPersister : entityPersisters.values()) {
            HibernateMetadata metadata = new HibernateMetadata(entityPersister.getClassMetadata());
            metadatas.put(metadata.getFullName(), metadata);
        }
    }

    @Override
    public <PK, T extends IEntity<PK>> T load(final Class<T> clz, final PK id) {
        T ret = (T) execute(new Callback<T>() {
            @Override
            public T call(StatelessSessionImpl session) {
                return (T) session.get(clz, (Serializable) id);
            }
        });
        if (ret instanceof PerPersist) {
            ((PerPersist) ret).afterLoad();
        }
        return ret;
    }

    @Override
    public <PK, T extends IEntity<PK>> PK save(Class<T> clz, final T entity) {
        if (entity instanceof PerPersist) {
            ((PerPersist) entity).beforePersist();
        }

        return (PK) execute(new Callback<Serializable>() {
            @Override
            public Serializable call(StatelessSessionImpl session) {
                return session.insert(entity);
            }
        });
    }

    @Override
    public <PK, T extends IEntity<PK>> void remove(Class<T> clz, final PK id) {
        final HibernateMetadata meta = (HibernateMetadata) metadatas.get(clz.getName());
        execute(new Callback<PK>() {
            @Override
            public PK call(StatelessSessionImpl session) {
                Query<T> queryObject = session.createQuery(meta.getDeleteHql());
                queryObject.setParameter(0, id);
                queryObject.executeUpdate();
                return id;
            }
        });
    }

    @Override
    public <PK, T extends IEntity<PK>> void remove(Class<T> clz, final T entity) {
        execute(new Callback<T>() {
            @Override
            public T call(StatelessSessionImpl session) {
                session.delete(entity);
                return entity;
            }
        });
    }

    @Override
    public <PK, T extends IEntity<PK>> T update(Class<T> clz, final T entity) {
        if (entity instanceof PerPersist) {
            ((PerPersist) entity).beforePersist();
        }
        return execute(new Callback<T>() {
            @Override
            public T call(StatelessSessionImpl session) {
                session.update(entity);
                return entity;
            }
        });
    }

    @Override
    public Collection<EntityMetadata> getAllMetadata() {
        return metadatas.values();
    }
}